package compilers.AST.expression;

import java.io.PrintStream;

import compilers.AST.Node;
import compilers.AST.types.MyType;
import compilers.AST.types.MyBool;
import compilers.AST.types.MyChar;
import compilers.AST.types.MyInt;
import compilers.AST.types.MyFloat;
import compilers.AST.types.TypeSpecifier;
import compilers.compiler.PGenerator;
import compilers.exceptions.SemanticalException;
import compilers.symbolTable.SymbolTableScope;

public class Negation extends Node {

	private int fNrNegations;
	private Term fTerm;
	
	public Negation() {
	}
	
	public Negation(int lineNr) {
		super(lineNr);
	}
	
	public void print(int identLevel) {
		println(identLevel, "Negation");
		if (fNrNegations > 0) {
			println(identLevel+1, "NrNegations = " + fNrNegations);
		} 
		fTerm.print(identLevel+1);
	}
	
	public void checkSemantics(SymbolTableScope currentScope) throws SemanticalException {
		fTerm.checkSemantics(currentScope);
		if (fNrNegations > 0) {
			if (fTerm.getResultType(currentScope) != TypeSpecifier.BOOLEAN) {
				throw new SemanticalException("The type in the negation expression should be boolean.", fLineNr);
			}
		}
	}
	
	public void setNrNegations(int nrNegations) {
		fNrNegations = nrNegations;
	}
	
	public void setTerm(Term term) {
		fTerm = term;
	}
	
	public int getResultType(SymbolTableScope currentScope) {
		return fTerm.getResultType(currentScope);
	}
	
	public int getOriginalType(SymbolTableScope currentScope) {
		return fTerm.getOriginalType(currentScope);
	}
	
	public boolean isConstant(SymbolTableScope currentScope) {
		return fTerm.isConstant(currentScope);
	}
	

	public boolean isArray(SymbolTableScope currentScope) {
		if (fNrNegations == 0) {
			return fTerm.isArray(currentScope);
		} else {
			return false;
		}
	}
	
	public String getArrayIdent(SymbolTableScope currentScope) {
		return fTerm.getArrayIdent(currentScope);
	}
	
	public MyType calculateValue(SymbolTableScope currentScope) {
		if(fNrNegations == 0) {
			return fTerm.calculateValue(currentScope);
		} else {
			MyType tmp = fTerm.calculateValue(currentScope);
			for(int i=0; i<fNrNegations; i++) {
				tmp = tmp.negate();
			}
			return tmp;
		}
	}
	
	public void generatePcode(PGenerator pGen, SymbolTableScope currentScope) {
		if (fTerm.isConstant(currentScope)) {
			TypeSpecifier resultType = new TypeSpecifier(fTerm.getResultType(currentScope));
			pGen.load(resultType.getPType(currentScope), fTerm.calculateValue(currentScope).valueToString());
		} else {
			fTerm.generatePcode(pGen, currentScope);
		}
		if (fNrNegations != 0) {
			for (int i = 0; i < fNrNegations; i++) {
				pGen.not();
			}
		}
		
		//Convert if necessary
		if(fTerm instanceof MyInt) {
			MyInt myInt = (MyInt) fTerm;
			pGen.conv(TypeSpecifier.getPType(myInt.getOriginalType(currentScope)), TypeSpecifier.getPType(myInt.getResultType(currentScope)));
		} else if(fTerm instanceof MyFloat) {
			MyFloat myFloat = (MyFloat) fTerm;
			pGen.conv(TypeSpecifier.getPType(myFloat.getOriginalType(currentScope)), TypeSpecifier.getPType(myFloat.getResultType(currentScope)));
		}
	}
}
